
/*
 * Labs.fx
 *
 * Created on Oct 10, 2009, 1:22:55 AM
 */

package GraphsWindowPackage;

import javafx.scene.control.Button;
import javafx.scene.control.ScrollView;
import AlertSystem.Window;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.part.NumberAxis;
import javafx.scene.chart.part.Side;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.LayoutInfo;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.util.Math;
import AlertSystem.DBConnection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.lang.Exception;
import GraphsWindowPackage.DocRecordPackage.DocRecord;
import GraphsWindowPackage.DocRecordPackage.Document;
import GraphsWindowPackage.LabPackage.Lab;
import GraphsWindowPackage.MedicationPackage.Order;
import GraphsWindowPackage.MedicationPackage.Medication;
import javafx.stage.Alert;
import javafx.date.DateTime;

/**
 * @author quang
 */

public class GraphsWindow extends Window {

    public var userName: String;
    public var patientId: String;

    var timeb4Assessment;

    def minTime: Number = 0;    //begin of time series
    var maxTime: Number = 0;   //end of time series

    var rollBackTime: Integer = 2; //number of days rolled back from maxTime

    var currentMaxTime: Number = maxTime - rollBackTime;

    var numAssessments: Integer = bind Math.round(rollBackTime / ajustedTimeStep) + 1;

    var startTimeIndicatorPos: Number = 0;
    var endTimeIndicatorPos: Number = chartDrawingAreaWidth;
    
    var startShownTime: Number;
    var endShownTime: Number;
    var numPixPerTimeUnit: Number = bind chartDrawingAreaWidth / (endShownTime - startShownTime);
    var rangerNumPixPerTimeUnit: Number = bind chartDrawingAreaWidth / (currentMaxTime - minTime);

    var timeScaler;
    var timeRanger;

    //sequence of assessment lines
    var assessments: Assessment[];
    var assessmentLines;

    //list of all document records. A document record is a sequence of documents
    var docRecords: DocRecord[] = null;

    var procedures: Medication[] = null;

    //list of all lab groups
    var labGroups: EntryGroup[] = null;

    //list of all medications
    var medications: Medication[] = null;

    //list of all active medications
    var activeMedications: Medication[] = null;

    //list of all non-active medications
    var nonActiveMedications: Medication[] = null;
    
    //updates startShowTime and endShownTime
    function updateTimeRange() {
        startShownTime = (startTimeIndicatorPos / rangerNumPixPerTimeUnit + minTime);
        endShownTime = (endTimeIndicatorPos / rangerNumPixPerTimeUnit + minTime);
        assessmentLines = createAssessmentLines();
    }

    function getDataForDocRecords (patientId: String, eventName: String) {
            var docRecordRS: ResultSet;
            var tableName: String;
            if (eventName == "Progress Notes") {tableName = "{inDBName}.progressnote"}
            else if (eventName == "OP Notes") {tableName = "{inDBName}.op_notes"}
            else if (eventName == "Microlab Reports") {tableName = "{inDBName}.microreport"}            
            else if (eventName == "EKG Reports") {tableName = "{inDBName}.ekgreport"}
            else if (eventName == "Radiology Reports") {tableName = "{inDBName}.radiologyreport"}
            else if (eventName == "HP and Consultation Summaries") {tableName = "{inDBName}.handp"};

            // data table should be ordered by numDays
            docRecordRS = DBConnection.query(connection, "SELECT text, numDays FROM {tableName}"
            " WHERE patientId = '{patientId}' ORDER BY dateTime");

            var documents: Document[];
            while (docRecordRS.next()) {
                insert Document {
                    time: docRecordRS.getDouble('numDays')
                    text: docRecordRS.getString('text')
                } into documents
            }

            insert DocRecord {
                name: eventName
                documents: documents
            } into this.docRecords;
    }

    function setupTimeRange(patientId: String){
        var paramsRS = DBConnection.query(connection, "SELECT maxNumDays FROM {inDBName}.params WHERE patientId = '{patientId}'");

        if (paramsRS.next()) {
            this.maxTime = paramsRS.getFloat('maxNumDays');
        }
        if (maxTime <= 1) {rollBackTime = 0}
        else if (maxTime <= 2) {rollBackTime = 1};
        
        currentMaxTime = maxTime - rollBackTime;
        startShownTime = minTime;
        endShownTime = currentMaxTime;
        updateTimeRange();        
    }

    function getDataForLabs (patientId: String) {
        //// READ DATA ABOUT PARAMETERS OF LABS /////////////////////////////////

        //var groupRS = DBConnection.query(connection, "SELECT DISTINCT groupName FROM {inDBName}.labparams WHERE patientId = '{patientId}'");
        var groupRS = DBConnection.query(connection,
        "SELECT DISTINCT groupName FROM {inDBName}.labparams l LEFT JOIN {inDBName}.groupmember_cler g ON l.eventtype = g.member "
        "WHERE patientId = '{patientId}' ORDER BY groupName") ;


        var groupName: String;
        var groupEntries: Entry[];

        var labParamsRS;

        var lab: Lab = null;
        var labRS: ResultSet;
        var eventtype: String = "";
        var labName: String = "";
        var timeSeq: Number[] = null;
        var labValSeq: Number[] = null;
        var ctgTimeSeq: Number[] = null;
        var ctgLabValSeq: String[] = null;

        var otherGroups: EntryGroup[] = null;

        while (groupRS.next()) {
            groupName = groupRS.getString('groupName');
            
            groupEntries = null;

            if (groupName != null) {
                labParamsRS = DBConnection.query(connection,
                "SELECT eventtype, l.labName, unit, lowerBound, upperBound, yStep, range1, range2 "
                "FROM {inDBName}.labparams l LEFT JOIN {inDBName}.groupmember_cler g ON l.eventtype = g.member "
                "WHERE patientId = '{patientId}' AND groupName = '{groupName}'");
            }
            else {
                labParamsRS = DBConnection.query(connection,
                "SELECT eventtype, l.labName, unit, lowerBound, upperBound, yStep, range1, range2 "
                "FROM {inDBName}.labparams l LEFT JOIN {inDBName}.groupmember_cler g ON l.eventtype = g.member "
                "WHERE patientId = '{patientId}' AND groupName IS NULL");
                groupName = 'OTHER';                
            }            

            while (labParamsRS.next()) {
                timeSeq = null;
                labValSeq = null;
                ctgTimeSeq = null;
                ctgLabValSeq = null;
                eventtype = labParamsRS.getString('eventtype');
                labName = labParamsRS.getString('labName');

                //read lab measurements with numeric values
                labRS = DBConnection.query(connection, "SELECT labVal, numDays FROM {inDBName}.lab WHERE eventtype = \"{eventtype}\""
                " AND patientId = '{patientId}' AND labVal IS NOT NULL ORDER BY eventtype, numDays");

                while(labRS.next()) {
                    insert labRS.getFloat('numDays') into timeSeq;
                    insert labRS.getFloat('labVal') into labValSeq;
                }

                //read lab measurements with categorical values
                labRS = DBConnection.query(connection, "SELECT result, numDays FROM {inDBName}.lab WHERE eventtype = \"{eventtype}\""
                " AND patientId = '{patientId}' AND labVal IS NULL ORDER BY eventtype, numDays");

                while(labRS.next()) {
                    insert labRS.getFloat('numDays') into ctgTimeSeq;
                    insert labRS.getString('result') into ctgLabValSeq;
                }

                lab = Lab {
                    name: labName
                    unit: labParamsRS.getString('unit')
                    lowerBound: labParamsRS.getFloat('lowerBound')
                    upperBound: labParamsRS.getFloat('upperBound')
                    yStep: labParamsRS.getFloat('yStep')
                    rangeMin: labParamsRS.getFloat('range1')
                    rangeMax: labParamsRS.getFloat('range2')
                    timeSeries: timeSeq
                    labValSeq: labValSeq
                    ctgTimeSeq: ctgTimeSeq
                    ctgLabValSeq: ctgLabValSeq
                }
                insert lab into groupEntries;
            } //end while (labParamsRS.next())

            if (groupName != 'OTHER')
                insert EntryGroup {
                    name: groupName
                    entries: groupEntries
                } into labGroups
            else
                insert EntryGroup {
                    name: groupName
                    entries: groupEntries
                } into otherGroups;
        } //end while (groupRS.next())
        for (j in [0..sizeof otherGroups - 1]) insert otherGroups[j] into labGroups;
    }

    function getDataForProcedures (patientId: String) {
        var procedureRS: ResultSet;

            procedureRS = DBConnection.query(connection,
            "SELECT text, isOnce, startNumDays, finishNumDays FROM {inDBName}.procedures"
            " WHERE patientId = '{patientId}'");

            var orders: Order[];
            while (procedureRS.next()) {
                insert Order {
                    isOnce: procedureRS.getBoolean('isOnce')
                    startTime: procedureRS.getDouble('startNumDays')
                    finishTime: procedureRS.getDouble('finishNumDays')
                    info: procedureRS.getString('text')
                } into orders
            }

            insert Medication {
               name: "Procedure List"
               orders: orders
            } into procedures
    }

    function getDataForMedications (patientId: String) {
        var medicationRS: ResultSet;
        var medName: String;
        
        def medNameRS = DBConnection.query(connection, "SELECT DISTINCT medName FROM {inDBName}.medication WHERE patientId = '{patientId}'");
        
        while (medNameRS.next()) {
            medName = medNameRS.getString('medName');

            medicationRS = DBConnection.query(connection, 
            "SELECT text, isOnce, startNumDays, finishNumDays FROM {inDBName}.medication"
            " WHERE patientId = '{patientId}' AND medName = '{medName}'");

            var orders: Order[];
            while (medicationRS.next()) {
                insert Order {
                    isOnce: medicationRS.getBoolean('isOnce')
                    startTime: medicationRS.getDouble('startNumDays')
                    finishTime: medicationRS.getDouble('finishNumDays')
                    info: medicationRS.getString('text')
                } into orders
            }

            insert Medication {
               name: medName
               orders: orders
            } into medications
         }

        this.updateActiveMedications();
    }

    //update active and non-active medication lists
    function updateActiveMedications () {
        delete activeMedications;
        delete nonActiveMedications;
        var startTime: Number;
        var finishTime: Number;
        var isActive = false;

        for (i in [0..sizeof medications - 1]) {
            isActive = false;
            for (j in [0..sizeof medications[i].orders - 1]) {
                startTime = medications[i].orders[j].startTime;
                finishTime = medications[i].orders[j].finishTime;
         
                if (startTime <= currentMaxTime and finishTime >= currentMaxTime - 1) {
                    insert medications[i] into activeMedications;
                    isActive = true;
                    break;
                }
            }
            if (isActive == false) insert medications[i] into nonActiveMedications;
        }
    }

    function getDataFromDB(patientId: String) {
        def docRecordList = ["Progress Notes", "OP Notes", "Radiology Reports",
        "EKG Reports", "Microlab Reports", "HP and Consultation Summaries"];

        try {
            this.setupTimeRange(patientId);

            for (i in [0..sizeof docRecordList - 1]) {
                this.getDataForDocRecords(patientId, docRecordList[i]);
            }

            this.getDataForProcedures(patientId);

            this.getDataForMedications(patientId);
            this.updateActiveMedications();

            this.getDataForLabs(patientId);
        } catch(ex : SQLException) {
                ex.printStackTrace();
            } catch(ex : Exception) {
                ex.printStackTrace();
            }
    }    

    function writeAssessments2DB(){
        try {
            for (i in [0..sizeof assessments - 1]) {
                DBConnection.updateQuery(connection, "UPDATE {surveyTable} SET diseaseChoice{i} = {assessments[i].diseaseChoice}, "
                "alertChoice{i} = {assessments[i].alertChoice}, "
                "time{i} = {assessments[i].assessmentTime} "
                "WHERE patientId = '{patientId}' AND userName = '{userName}';" );
            }

            var totalTime: Integer = 0;
            for (j in [0..sizeof assessments -1]){
                totalTime += assessments[j].assessmentTime;
            }

            DBConnection.updateQuery(connection,"UPDATE {surveyTable} SET isFinished = 1, "
            "totalTime = {totalTime} "
            "WHERE patientId = '{patientId}' AND userName = '{userName}';");
        } catch(ex : SQLException) {
                ex.printStackTrace();
            } catch(ex : Exception) {
                ex.printStackTrace();
            }
    }

    function createDocRecordGroupView() {
        return EntryGroupView {
            startShownTime: bind startShownTime
            endShownTime: bind endShownTime
            numPixPerTimeUnit: bind numPixPerTimeUnit
            entryGroup: EntryGroup {
                name: "REPORTS AND NOTES"
                entries: docRecords
            }
        }        
    }

    function createLabGroupsView() {
        return VBox {
            content: [
                Text {
                    font: font2
                    content: 'LABS\n'
                },                
                for (i in [0..sizeof labGroups - 1]) {
                    EntryGroupView {
                        startShownTime: bind startShownTime
                        endShownTime: bind endShownTime
                        numPixPerTimeUnit: bind numPixPerTimeUnit
                        entryGroup: EntryGroup {
                            name: labGroups[i].name
                            entries: labGroups[i].entries                            
                        }
                    }
                }
            ]
        }
    }

    function createMedGroupsView(activeMedications: Medication[], nonActiveMedications: Medication[]) {
        return VBox {
            content: [
                EntryGroupView {
                    startShownTime: bind startShownTime
                    endShownTime: bind endShownTime
                    numPixPerTimeUnit: bind numPixPerTimeUnit
                    entryGroup: EntryGroup {
                        name: "ACTIVE MEDICATIONS"
                        entries: activeMedications
                    }
                },
                EntryGroupView {
                    startShownTime: bind startShownTime
                    endShownTime: bind endShownTime
                    numPixPerTimeUnit: bind numPixPerTimeUnit
                    entryGroup: EntryGroup {
                        name: "NON-ACTIVE MEDICATIONS"
                        entries: nonActiveMedications
                    }
                }
            ]
        }
    }

    function createProcedureGroupView() {
        return EntryGroupView {
            startShownTime: bind startShownTime
            endShownTime: bind endShownTime
            numPixPerTimeUnit: bind numPixPerTimeUnit
            entryGroup: EntryGroup {
                name: "PROCEDURES"
                entries: bind procedures
            }
        }
    }

    function createAssessmentLines() {
        var xCoord;
        for (i in [0..sizeof assessments - 1]) {
            xCoord = (assessments[i].assessmentNumDays - startShownTime) * numPixPerTimeUnit + chartGap;
            VBox {
                translateX: this.chartDrawingAreaXOffset + xCoord
                translateY: headerBgHeight// - 25 //compensation for probability labels
                content: [
//                    Text {
//                        translateX: -chartGap
//                        content: "{assessments[i].diseaseChoice}\n"
//                        "{assessments[i].alertChoice}"
//                        opacity: if (xCoord >=0 and xCoord <= this.chartDrawingAreaWidth + chartGap + 10) 1.0 else 0.0
//                    },
                    Line {
                        startX: 0//xCoord
                        startY: 0
                        endX: 0//xCoord
                        endY: screenHeight - headerBgHeight - windowContentYOffset - 3
                        strokeWidth: if (i < numAssessments - 1) 1 else 3
                        stroke: if (assessments[i].alertChoice > alertChoieThreshold) Color.RED else Color.GREEN
//                        stroke: Color.GREEN
                        opacity: if (xCoord >=0 and xCoord <= this.chartDrawingAreaWidth + chartGap) 1.0 else 0.0
                    }
                ]
            }
        }
    }

    function createStartTimeIndicator(): Group {

        var X: Number;

        var time: Number = bind (startTimeIndicatorPos / rangerNumPixPerTimeUnit + minTime);

        def indicatorValue = Text {
           content: bind "{Math.round(Math.floor(time))} days\n{Math.round ((time - Math.floor(time)) * 24)} hours";
           translateX: bind indicator.translateX
           translateY: -indicatorValueOffset
           fill: Color.BLACK
        };

        def scaleLine = Line {
            startX: bind startTimeIndicatorPos
            startY: 0
            endX: 0
            endY: 0.45 * this.timeRangerHeight
            fill: Color.GREEN
            strokeWidth: 1
            stroke: Color.BLACK
        }

        def indicator = Circle {
                centerX: 0
                centerY: 0
                radius: timeIndicatorRadius
                fill: Color.GREEN
                scaleX: 0.5

                onMouseClicked: function(event) {
                    event.node.translateX = 0;
                    startTimeIndicatorPos = 0;
                    updateTimeRange();
                    timeScaler = createTimeScaler();
//                    System.gc();
                }

                onMousePressed: function(event) {
                   X = event.sceneX - event.node.translateX;
                }

                onMouseDragged: function(event) {
                    if (event.sceneX - X < 0) {
                           event.node.translateX = 0;
                        } else
                        { if (event.sceneX - X > chartDrawingAreaWidth){
                               event.node.translateX = chartDrawingAreaWidth;
                            } else {
                               event.node.translateX = event.sceneX - X;
                               startTimeIndicatorPos = event.sceneX - chartDrawingAreaXOffset;
                            }
                        }
                }

                onMouseReleased: function( e: MouseEvent ):Void {
                    updateTimeRange();
                    timeScaler = createTimeScaler();
//                    System.gc();
                }
            }

        return Group {
            content: [indicator, indicatorValue, scaleLine]
        }
    }

    function createEndTimeIndicator(): Group {

        endTimeIndicatorPos = chartDrawingAreaWidth;

        var X: Number;

        var time: Number = bind (endTimeIndicatorPos / rangerNumPixPerTimeUnit + minTime);

        def indicatorValue = Text {
           content: bind "{Math.round(Math.floor(time))} days\n{Math.round ((time - Math.floor(time)) * 24)} hours";
           translateX: bind indicator.translateX + chartDrawingAreaWidth
           translateY: -indicatorValueOffset
           fill: Color.BLACK
        };

        def scaleLine = Line {
            startX: bind endTimeIndicatorPos
            startY: 0
            endX: chartDrawingAreaWidth
            endY: 0.45 * this.timeRangerHeight
            fill: Color.BLUE
            strokeWidth: 1
            stroke: Color.BLACK
        }

        def indicator = Circle {
                centerX: endTimeIndicatorPos 
                centerY: 0 
                radius: timeIndicatorRadius
                fill: Color.DARKBLUE
                scaleX: 0.5

                onMouseClicked: function(event) {
                    event.node.translateX = 0;
                    endTimeIndicatorPos = chartDrawingAreaWidth;
                    updateTimeRange();
                    timeScaler = createTimeScaler();
//                    System.gc();
                }

                onMousePressed: function(event) {
                   X = event.sceneX - event.node.translateX;
                }

                onMouseDragged:
                function(event) {                   
                        if (event.sceneX - X < -chartDrawingAreaWidth) {
                               event.node.translateX = -chartDrawingAreaWidth
                            } else { if (event.sceneX - X > 0){
                               event.node.translateX = 0;
                            } else {
                               event.node.translateX = event.sceneX - X;
                               endTimeIndicatorPos = event.sceneX - chartDrawingAreaXOffset;
                            }
                        }
                }

                onMouseReleased: function( e: MouseEvent ):Void {
                    updateTimeRange();
                    timeScaler = createTimeScaler();
//                    System.gc();
                }
            }

        return Group {
            content: [indicator, indicatorValue, scaleLine]
        }
    }

    function getTimeTickUnit(startTime: Number, endTime: Number): Number {
        return Math.ceil((endTime - startTime) / 2) / 6;
    }

    function createTimeRanger() {

        def startTimeIndicator = createStartTimeIndicator ();
        def endTimeIndicator = createEndTimeIndicator ();

        def ranger = LineChart {
//            translateX: chartDrawingAreaXOffset - chartGap
            layoutInfo: LayoutInfo {
                height: timeRangerHeight
                width: chartDrawingAreaWidth + chartGap * 4
            }

            xAxis: NumberAxis {
                side: Side.BOTTOM
                lowerBound: minTime
                upperBound: bind currentMaxTime
                tickUnit: bind getTimeTickUnit(minTime, currentMaxTime)
                minorTickCount: 4 //Math.round(3*getTimeTickUnit(minTime, currentMaxTime))
                //formatTickLabel: function(value):String { "{(value as Float) as Integer}d\n" }
                formatTickLabel: function(value):String { "{Math.round(Math.floor(value))}d\n{Math.round ((value - Math.floor(value)) * 24)}h"; }
                tickLabelsVisible: true
                tickMarkVisible: true
            }
            yAxis: NumberAxis {
                tickLabelsVisible: false
                tickMarkVisible: false
            }

            customBackgroundContent: Group {
                content: bind [startTimeIndicator, endTimeIndicator ]
            }

            legendVisible: false
        }

        return ranger;
    }

    function createTimeScaler(): LineChart {

        return LineChart {
//            translateX: chartDrawingAreaXOffset - chartGap
            layoutInfo: LayoutInfo {
                height: timeRangerHeight
                width: chartDrawingAreaWidth + chartGap * 4
            }

            xAxis: NumberAxis {
                side: Side.BOTTOM
                lowerBound: bind startShownTime
                upperBound: bind endShownTime
                tickUnit: bind getTimeTickUnit(startShownTime, endShownTime)
                minorTickCount: 4 //Math.round(3*getTimeTickUnit(startShownTime, endShownTime))
                formatTickLabel: function(value):String { "{Math.round(Math.floor(value))}d\n{Math.round ((value - Math.floor(value)) * 24)}h"; }
                tickLabelsVisible: true
                tickMarkVisible: true
            }
            yAxis: NumberAxis {
                tickLabelsVisible: false
                tickMarkVisible: false
            }

            legendVisible: false
        }
    }

    function createAssessmentBlock(shown: Boolean) {
        var assessmentTime: Integer = 0;

        def bgRect = Rectangle {
            x: 0, y: 0
            width: assessmentBoxWidth
            height: assessmentBoxHeight
            fill: Color.WHITE
            stroke: Color.GRAY
            strokeWidth: 1
        }

        def requestText = Text {
            font: font2
            content: "For the current time point, please assess the following:"
            wrappingWidth: assessmentBoxWidth
        }       

        //-----------------------------------------------
        
        var diseaseChoiceBlock = ChoiceBlock {
            questionText: "1. The chance this patient has {disease} or will develop {disease} during this hospital stay"
            choices: [ "Very-Low", "Lower-than-Baseline", "Baseline", "Higher-than-Baseline", "Likely", "Very-Likely" ]

            infos: [
                "The chance of HIT is very low. I will be surprised if the patient develops HIT",
                "The chance of HIT is somewhat lower than that expected for the population of post-surgical cardiac patients",
                "The chance of HIT is comparable to that expected for the population of post-surgical cardiac patients",
                "The chance of HIT is somewhat  higher than that expected for the population of post-surgical cardiac patients",
                "The chance of HIT is high enough to raise a HIT concern",
                "The chance of HIT is very high - HIT is really a serious and imminent concern"
            ]
        }
        //-----------------------------------------------

        var alertChoiceBlock = ChoiceBlock {
            questionText: "2. Assume you have received an alert (via standard route) that this patient is at risk of {disease}.\n"
            "How would you assess this alert ?"
            choices: [ "Strongly-Disagree", "Disagree", "Neutral", "Agree", "Strongly-Agree" ]
            infos: [
                "I strongly disagree with raising the HIT alert for this patient. There is no evidence that would justify the alert",
                "I disagree with raising the HIT alert for this patient. There are some signs of possible HIT risk but clearly they are not sufficient to alert on",
                "I do not think raising an alert now is appropriate at this time, but there are some concerning signs and I think the reassessment of the patient with respect to HIT again within next 24 hours is appropriate",
                "I agree with raising an alert. I am not clearly convinced about the HIT, but the symptoms are concerning enough to warrant taking action for suspected HIT",
                "I strongly agree with the alert. I think this patient is at the risk of having or developing HIT and the HIT alert should be raised"
            ]
        }
        //-----------------------------------------------        

        def confirmButton = Button {
            font: font2
            text: "Confirm"
            
            action: function () {
                if (diseaseChoiceBlock.getSelectedChoice() != -1 and alertChoiceBlock.getSelectedChoice() != -1
                and sizeof assessments < numAssessments) {
                    assessmentTime = Math.round((DateTime{}.instant - timeb4Assessment) / 1000);
                    timeb4Assessment = DateTime{}.instant;

                    insert Assessment {
                        diseaseChoice: diseaseChoiceBlock.getSelectedChoice()
                        alertChoice: alertChoiceBlock.getSelectedChoice()
                        assessmentNumDays: currentMaxTime
                        assessmentTime: assessmentTime
                    } into assessments;

                    if (sizeof assessments < numAssessments) {
                        currentMaxTime = currentMaxTime + ajustedTimeStep;
                        this.updateTimeRange();
                        this.updateActiveMedications();

                        alertChoiceBlock.reset();
                        diseaseChoiceBlock.reset();
                    }
                    else {                        
                        assessmentLines = createAssessmentLines();
                        writeAssessments2DB();
                        Alert.inform("All required assessments for current patient were given.\n"
                        "Click on 'Next Patient' button to proceed.");
                    }                    
//                    System.gc();
                }
                else if (sizeof assessments >= numAssessments) {
                    Alert.inform("All required assessments for current patient were given.\n"
                    "Click on 'Next Patient' button at the top right corner of screen to proceed.");
                }
            }
        }
        //-----------------------------------------------
        
        return Group {
//            opacity: bind if (shown) 1.0 else 0.0
            content: if (shown)
            [
                bgRect,
                VBox {
                    content: [  requestText, diseaseChoiceBlock, alertChoiceBlock, confirmButton ]
                    spacing: 15
                }                
            ] else []
        }
    }

    //main function: creates graphical view for everything in this tab
    protected override function create():Node {
        timeb4Assessment = DateTime{}.instant;
        
        getDataFromDB(patientId);

        //header background
        def headerBgRect = Rectangle {
            width: screenWidth
            height: headerBgHeight
            fill: Color.WHITE
            stroke: Color.LIGHTGRAY
        }

        timeRanger = createTimeRanger();
        timeScaler = createTimeScaler();

        var showAsmtWindow = false;
        def assessmentButton = Button {
            text: "Assessment Window"
            font: font2
            action: function() {
                     showAsmtWindow = not showAsmtWindow;
                }
        }
        
        var assessmentBlock = bind createAssessmentBlock(showAsmtWindow);

        def rulers = VBox {
                    translateX: chartDrawingAreaXOffset
                    spacing: 15
                    content: bind [timeRanger, timeScaler]
                }

        var medGroupsView = bind createMedGroupsView(activeMedications, nonActiveMedications);

        def entriesViewFrame = ScrollView {
            layoutY: bind headerBgHeight
            node: VBox {
                content: bind [
                    createDocRecordGroupView(),
                    createProcedureGroupView(),
                    createLabGroupsView(),
                    medGroupsView
                ]
            }
            layoutInfo: LayoutInfo {
                width: screenWidth
                height: screenHeight - headerBgHeight - windowContentYOffset
            }
        }

        return Group {
            translateY: windowContentYOffset
            content: bind [
                headerBgRect,
                rulers,
                entriesViewFrame,
                assessmentLines,
                VBox {
                    translateY: headerBgHeight * 0.2 - font2.size
                    content: [
                        assessmentButton,
                        assessmentBlock
                    ]
                }
            ]            
        }
    }
}
